package com.danco.training.zaneuskaya.service.impl;

import java.io.IOException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.danco.training.zaneuskaya.annotation.ConfigProperty;
import com.danco.training.zaneuskaya.annotation.ConfigPropertyAnalizer;
import com.danco.training.zaneuskaya.annotation.Type;
import com.danco.training.zaneuskaya.api.BookService;
import com.danco.training.zaneuskaya.connection.HibernateSessionUtil;
import com.danco.training.zaneuskaya.connection.dao.BookDao;
import com.danco.training.zaneuskaya.connection.dao.ClaimDao;
import com.danco.training.zaneuskaya.connection.dao.OrderDao;
import com.danco.training.zaneuskaya.connection.dao.impl.BookDaoImpl;
import com.danco.training.zaneuskaya.connection.dao.impl.ClaimDaoImpl;
import com.danco.training.zaneuskaya.connection.dao.impl.OrderDaoImpl;
import com.danco.training.zaneuskaya.cswworker.CSVReader;
import com.danco.training.zaneuskaya.cswworker.CSVSaver;
import com.danco.training.zaneuskaya.cswworker.Separator;
import com.danco.training.zaneuskaya.domain.Book;
import com.danco.training.zaneuskaya.domain.Claim;
import com.danco.training.zaneuskaya.domain.Order;

public class BookServiceImpl implements BookService {

	@ConfigProperty(configName = { "test.properties" }, propertyName = "months", type = Type.INTEGER)
	public static int MONTH_NUMBER = 0;

	@ConfigProperty(configName = { "bookshop.properties" }, propertyName = "setclaim", type = Type.BOOLEAN)
	public static boolean EXECUTE_CLAIM = false;

	public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
			"yyyy.MM.dd");

	private BookDao bookDao = new BookDaoImpl();
	private ClaimDao claimDao = new ClaimDaoImpl();
	private OrderDao orderDao = new OrderDaoImpl();

	private static final Logger logger = Logger
			.getLogger(BookServiceImpl.class);

	private static BookServiceImpl bookStorageSeviceImplInstance;

	

	public BookServiceImpl() { // public - client send message with className ->
								// reflection -> newInstance()
		ConfigPropertyAnalizer.configPropertyAnalizer(this);

	}

	public Book getById(Integer id) {
		Session session = null;
		Transaction transaction = null;
		Book book = null;
		try {
			session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			book = bookDao.read(session, id);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
    			transaction.rollback();
    		}catch(RuntimeException rbe){
    			logger.error("Couldn’t roll back transaction", rbe);
    		}
		}
		return book;
	}

	public List<Book> getList() {
		Transaction transaction = null;
		Session session = null;
		List<Book> books = new ArrayList<Book>();
		logger.trace("Book list");
		try {
			session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			books.addAll(bookDao.readAll(session));
			transaction.commit();
		}catch(RuntimeException e){
    		try{
    			transaction.rollback();
    		}catch(RuntimeException rbe){
    			logger.error("Couldn’t roll back transaction", rbe);
    		}
    		throw e;
    	}
		return books;
	}

	public List<Book> getSomeMonthsUnsoldBooks() {
		Session session = null;
		Transaction transaction = null;
		List<Book> res = new ArrayList<Book>();
		try {
			session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			res = bookDao.readSomeMonthsUnsoldBooks(session, MONTH_NUMBER);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
    			transaction.rollback();
    		}catch(RuntimeException rbe){
    			logger.error("Couldn’t roll back transaction", rbe);
    		}

		}
		logger.trace("MONTH_NUMBER " + " months unsold books are founded");
		return res;

	}

	public List<Book> getBooksSortedByAlphabet() {
		Session session = null;
		Transaction transaction = null;
		List<Book> res = new ArrayList<Book>();
		try {
			session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			res = bookDao.readBooksSortedByAlphabet(session);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
    			transaction.rollback();
    		}catch(RuntimeException rbe){
    			logger.error("Couldn’t roll back transaction", rbe);
    		}
		}
		return res;
	}

	public List<Book> getBooksSortedByPublicationDate() {
		Session session = null;
		Transaction transaction = null;
		List<Book> res = new ArrayList<Book>();
		try {
			session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			res = bookDao.readBooksSortedByPublicationDate(session);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
    			transaction.rollback();
    		}catch(RuntimeException rbe){
    			logger.error("Couldn’t roll back transaction", rbe);
    		}

		}
		return res;
	}

	public List<Book> getBooksSortedByPrice() {
		List<Book> list = null;
		Transaction transaction = null;
		try{
		Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
		transaction = session.beginTransaction();
		list = bookDao.readBooksSortedByPrice(session);
		transaction.commit();
		}
		catch (HibernateException e){

		try{
			transaction.rollback();
		}catch(RuntimeException rbe){
			logger.error("Couldn’t roll back transaction", rbe);
		}
		logger.error(e.getMessage(), e);
		}
		return list;

	}

	public List<Book> getBooksSortedByAvailability() {
		Transaction transaction = null;
		List<Book> list = new ArrayList<Book>();
		try{
		Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
		transaction = session.beginTransaction();
		list = bookDao.readBooksSortedByAvailability(session);
transaction.commit();
		}
		catch (HibernateException e){

			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}
			logger.error(e.getMessage(), e);
			}
		return list;
	}

	public List<Book> getUnsoldBooksSortedByReceiptDate() {
		
		Transaction transaction = null;
		List<Book> list = new ArrayList<Book>();
		try{
		Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
		transaction = session.beginTransaction();
		list = bookDao
				.readUnsoldBooksSortedByReceiptDate(session, MONTH_NUMBER);
		transaction.commit();
		}
		catch (HibernateException e){

			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}
			logger.error(e.getMessage(), e);
			}
		return list;
	}

	public List<Book> getUnsoldBooksSortedByPrice() {
		Transaction transaction = null;
		List<Book> list = new ArrayList<Book>();
		try {
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			list = bookDao.readUnsoldBooksSortedByPrice(session, MONTH_NUMBER);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}
		}
		return list;

	}

	public List<Book> getAvailableBooks() {
		Transaction transaction = null;
		List<Book> list = new ArrayList<Book>();
		try {
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			list = bookDao.readAvailableBooks(session);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}

		}
		return list;

	}

	public List<Book> getNotAvailableBooks() {
		Transaction transaction = null;
		List<Book> list = new ArrayList<Book>();
		try {
			
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();
			list = bookDao.readNotAvailableBooks(session);
			transaction.commit();
		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}
		}
		return list;
	}

	public void addEntity(Book book) {
		Transaction transaction = null;
		Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();

		try {
			transaction = session.beginTransaction();
			bookDao.create(session, book);
			transaction.commit();

		} catch (HibernateException e) {
			logger.error(e.getMessage(), e);
			try{
				transaction.rollback();
			}catch(RuntimeException rbe){
				logger.error("Couldn’t roll back transaction", rbe);
			}
		}
	}

	public void removeEntityById(Integer id) {
		Transaction transaction = null;
		try {
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();

			transaction = session.beginTransaction();
			Book book = bookDao.read(session, id);
			List<Claim> claims = claimDao.findClaimByBook(session, book);
			if (claims != null) {
				for (Claim c : claims) {
					claimDao.delete(session, c);
				}
			}
			List<Order> orders = orderDao.findOrderByBook(session, book);
			if (orders != null) {
				for (Order order : orders) {
					orderDao.removeBookOrder(session, order, book);

				}
			}
			bookDao.delete(session, book);

			transaction.commit();

		} catch (HibernateException e) {
			transaction.rollback();
			logger.error(e.getMessage(), e);
		}
	}

	public void saveEntityListToFile(String file) {
		ArrayList<String> res = new ArrayList<String>();
		try {
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			for (Book b : bookDao.readAll(session)) {
				res.add(entityDetails(b));
			}
		} catch (HibernateException e1) {
			logger.error(e1.getMessage(), e1);
		}
		try {
			CSVSaver.saveListToFile(file, res);
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

	public List<Book> readEntityList(String file) {
		ArrayList<Book> res = new ArrayList<Book>();

		Date date = null;

		for (int i = 0; i < CSVReader.readFile(file).length; i++) {
			String book[] = CSVReader.readFile(file)[i]
					.split(Separator.SEPARATOR.getS());
			try {

				date = DATE_FORMAT.parse(book[3]);
			} catch (ParseException e) {
				logger.error(e.getMessage(), e);
			}
			Book b = new Book(Integer.parseInt(book[0]), book[1], book[2], date);
			res.add(b);

		}
		return res;
	}

	public static BookServiceImpl getBookStorageSeviceImplInstance() {

		if (bookStorageSeviceImplInstance == null) {
			bookStorageSeviceImplInstance = new BookServiceImpl();

		}
		return bookStorageSeviceImplInstance;
	}

	public String entityDetails(Book t) {

		StringBuffer str = new StringBuffer();

		str.append(t.getId());
		str.append(Separator.SEPARATOR.getS());
		str.append(t.getTitle());
		str.append(Separator.SEPARATOR.getS());
		str.append(t.getAuthor());
		str.append(Separator.SEPARATOR.getS());
		str.append(DATE_FORMAT.format(t.getPublicationDate()));

		return str.toString();
	}

	public void updateEntity(Book e) {
		
		Transaction transaction = null;
		try {
			Session session = HibernateSessionUtil.getSessionFactory().getCurrentSession();
			transaction = session.beginTransaction();

			bookDao.update(session, e);
			List<Claim> claims = claimDao.findClaimByBook(session, e);
			if (!claims.isEmpty()) {
				for (Claim claim : claims) {
					if (claim.getClaimAmount() <= e.getQuantity()) {
						claimDao.executeClaim(session, claim);
					}
				}

			}

			transaction.commit();

		} catch (HibernateException e1) {
			transaction.rollback();

			logger.error(e1.getMessage(), e1);
		}

	}

}
